/**
 * 
 */
package soa.security.mgr.service.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.ldap.NameAlreadyBoundException;
import org.springframework.ldap.NameNotFoundException;
import org.springframework.ldap.NamingException;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.query.SearchScope;
import org.springframework.stereotype.Service;

import soa.security.Constants;
import soa.security.dao.AccountDao;
import soa.security.dao.ApplsystemDao;
import soa.security.dao.DepartmentDao;
import soa.security.dao.GroupDao;
import soa.security.dao.OrganizationDao;
import soa.security.dao.OrganizationUnitDao;
import soa.security.dao.PersonDao;
import soa.security.dao.PositionDao;
import soa.security.dao.RegionDao;
import soa.security.dao.RoleDao;
import soa.security.dao.impl.LdaptiveSupport;
import soa.security.domain.Account;
import soa.security.domain.Applsystem;
import soa.security.domain.Department;
import soa.security.domain.Group;
import soa.security.domain.Organization;
import soa.security.domain.OrganizationUnit;
import soa.security.domain.Person;
import soa.security.domain.Position;
import soa.security.domain.Region;
import soa.security.domain.SoaTop;
import soa.security.exception.ImportException;
import soa.security.exception.OrganizationException;
import soa.security.jpa.domain.Change;
import soa.security.jpa.service.ChangeMgrService;
import soa.security.mgr.service.base.BaseMgrService;

/**
 * @author Cre.Gu
 *
 */
@Service
public class BaseMgrServiceImpl implements BaseMgrService {
	protected final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(this.getClass());

	public SearchScope defaultSearchScope = SearchScope.ONELEVEL;
	public SearchScope subtreeSearchScope = SearchScope.SUBTREE;
	public org.ldaptive.SearchScope defaultSearchScope4Ldaptive = org.ldaptive.SearchScope.ONELEVEL;

	@Resource
	protected LdaptiveSupport ldaptiveSupport;
	@Resource
	protected OrganizationDao organizationDao;
	@Resource
	protected RegionDao regionDao;
	@Resource
	protected DepartmentDao departmentDao;
	@Resource
	protected PersonDao personDao;
	@Resource
	protected AccountDao accountDao;
	@Resource
	protected PositionDao positionDao;
	@Resource
	protected GroupDao groupDao;
	@Resource
	protected RoleDao roleDao;
	@Resource
	protected OrganizationUnitDao organizationUnitDao;
	@Resource
	protected LdapTemplate ldapTemplate;
	@Resource
	protected ChangeMgrService changeMgrService;
	@Resource
	protected ApplsystemDao applsystemDao;

	/*
	 * (non-Javadoc)
	 * 
	 * @see soa.security.mgr.service.base.BaseMgrService#exist(java.lang.String)
	 */
	@Override
	public boolean exist(String organizationId) {
		return organizationDao.exist(organizationId);
	}

	@Override
	public Organization findOrganization(String organizationId) {
		return organizationDao.findByPrimaryKey(organizationId);
	}
	
	@Override
	public Region findRegion(String regionId) {
		return regionDao.findByPrimaryKey(regionId);
	}

	@Override
	public boolean modelOverrided() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public void validateOrganization(String organizationId) throws OrganizationException {
		// 机构是否存在
		if (StringUtils.isBlank(organizationId))
			throw new OrganizationException("机构id不能为空");

		if (!exist(organizationId))
			throw new OrganizationException("机构[" + organizationId + "]不存在");

	}

	public void validateAccount(String baseDn, String uid) throws OrganizationException {
		if (accountDao.findByPrimaryKey(baseDn, uid) == null)
			throw new OrganizationException("账号[" + uid + "]不存在");
	}

	public void validateDepartment(String baseDn, String id) throws OrganizationException {
		if (departmentDao.findByPrimaryKey(baseDn, id) == null)
			throw new OrganizationException("部门[" + id + "]不存在");
	}

	public void validatePerson(String baseDn, String id) throws OrganizationException {
		if (personDao.findByPrimaryKey(baseDn, id) == null)
			throw new OrganizationException("人员[" + id + "]不存在");
	}
	
	public void validateApplsystem(String baseDn, String id) throws OrganizationException {
		if (applsystemDao.findByPrimaryKey(baseDn, id) == null)
			throw new OrganizationException("应用系统[" + id + "]不存在");
	}

	public void validatePosition(String baseDn, String id) throws OrganizationException {
		if (positionDao.findByPrimaryKey(baseDn, id) == null)
			throw new OrganizationException("岗位[" + id + "]不存在");
	}

	public void validateGroup(String baseDn, String id) throws OrganizationException {
		if (groupDao.findByPrimaryKey(baseDn, id) == null)
			throw new OrganizationException("用户组[" + id + "]不存在");
	}

	public void validateRole(String baseDn, String id) throws OrganizationException {
		if (roleDao.findByPrimaryKey(baseDn, id) == null)
			throw new OrganizationException("角色[" + id + "]不存在");
	}

	public OrganizationUnit gainOrganizationUnit(String organizationId, String ou) throws OrganizationException {
		OrganizationUnit unit = organizationUnitDao.findBy(findOrganization(organizationId), ou);
		if (unit == null)
			throw new OrganizationException("请先初始化机构LDAP");

		return unit;
	}
	
	public OrganizationUnit gainOrganizationUnit_region(String regionDn, String ou) throws OrganizationException {
		OrganizationUnit unit = organizationUnitDao.findBy(regionDao.findByDn(regionDn), ou);
		if (unit == null)
			throw new OrganizationException("请先初始化地区LDAP");

		return unit;
	}

	public void wrapException(Exception ex) throws OrganizationException {
		try {
			throw ex;
		} catch (NameAlreadyBoundException e) {
			throw new OrganizationException(OrganizationException.ERROR_02, e);
		} catch (NameNotFoundException e) {
			throw new OrganizationException(OrganizationException.NOTFOUND, e);
		} catch (NamingException e) {
			throw new OrganizationException(OrganizationException.ERROR_LDAP, e);
		} catch (IllegalArgumentException e) {
			throw new OrganizationException(e.getMessage(), e);
		} catch (OrganizationException e) {
			throw e;
		} catch (ImportException e) {
			throw new OrganizationException(e.getMessage());
		} catch (Exception e) {
			throw new OrganizationException(OrganizationException.ERROR_OTHER, e);
		}
	}

	public void printException(Exception ex) {
		try {
			log.debug("报错", ex);

			throw ex;
		} catch (NameAlreadyBoundException e) {
			System.out.println(OrganizationException.ERROR_02);
		} catch (NameNotFoundException e) {
			System.out.println(OrganizationException.NOTFOUND);
		} catch (NamingException e) {
			System.out.println(OrganizationException.ERROR_LDAP);
		} catch (IllegalArgumentException e) {
			System.out.println(e.getMessage());
		} catch (OrganizationException e) {
			System.out.println(OrganizationException.ERROR_OTHER);
		} catch (Exception e) {
			System.out.println(OrganizationException.ERROR_OTHER);
		}
	}

	@Override
	public String findMaxNumber(Class<?> clazz) {
		try {
			return regionDao.findMaxNumber(clazz);
		} catch (Exception e) {
			printException(e);
		}
		return null;
	}

	protected List<Person> departmentAllPersons(String organizationId, String departmentId)
			throws OrganizationException {
		return departmentAllPersons(organizationId, departmentId, null);
	}

	protected List<Person> departmentAllPersons(String organizationId, String departmentId,
			Map<String, Object> parameters) throws OrganizationException {
		List<Person> list = new ArrayList<Person>();
		try {
			if (StringUtils.isBlank(departmentId))
				return list;

			validateOrganization(organizationId);
			OrganizationUnit deUnit = gainOrganizationUnit(organizationId, Constants.DEPARTMENT);
			validateDepartment(deUnit.getDn().toString(), departmentId);

			List<String> pids = departmentDao
					.allPersons(departmentDao.findByPrimaryKey(deUnit.getDn().toString(), departmentId));

			for (String string : pids) {
				list.add(personDao.findByDn(string));
			}
		} catch (Exception e) {
			wrapException(e);
		}
		return list;
	}

	protected List<Applsystem> departmentAllApplsystems(String organizationId, String departmentId,
			Map<String, Object> parameters) throws OrganizationException {
		List<Applsystem> list = new ArrayList<Applsystem>();
		try {
			if (StringUtils.isBlank(departmentId))
				return list;

			validateOrganization(organizationId);
			OrganizationUnit deUnit = gainOrganizationUnit(organizationId, Constants.DEPARTMENT);
			validateDepartment(deUnit.getDn().toString(), departmentId);

			List<String> pids = departmentDao
					.allApplsystem(departmentDao.findByPrimaryKey(deUnit.getDn().toString(), departmentId));

			for (String string : pids) {
				list.add(applsystemDao.findByDn(string));
			}
		} catch (Exception e) {
			wrapException(e);
		}
		return list;
	}

	protected List<Department> departmentAllDepartments(String organizationId) throws OrganizationException {
		return departmentAllDepartments(organizationId, null, false);
	}

	protected List<Department> departmentAllDepartments(String organizationId, Map<String, Object> parameters,
			boolean subTree) throws OrganizationException {
		List<Department> results = new ArrayList<Department>();
		try {
			validateOrganization(organizationId);

			if (subTree) {
				List<Organization> oneOrg = oneOrg(organizationId);
				for (Organization or : oneOrg) {
					results.addAll(doDepartmentAllDepartments(or.getBid(), parameters));
				}
			} else {
				results = doDepartmentAllDepartments(organizationId, parameters);
			}
		} catch (Exception e) {
			wrapException(e);
		}
		return results;
	}

	protected List<Department> doDepartmentAllDepartments(String organizationId, Map<String, Object> parameters) {
		List<Department> results = new ArrayList<Department>();

		OrganizationUnit unit = gainOrganizationUnit(organizationId, Constants.DEPARTMENT);

		results = departmentDao.allDepartments(unit.getDn().toString(), defaultSearchScope, parameters);

		sortResult(results);
		return results;
	}

	protected List<String> positionPersons(String organizationId, String positionId) {
		List<String> list = new ArrayList<String>();
		try {
			if (StringUtils.isBlank(positionId))
				return list;
			validateOrganization(organizationId);
			OrganizationUnit deUnit = gainOrganizationUnit(organizationId, Constants.POSITION);
			validatePosition(deUnit.getDn().toString(), positionId);

			List<String> pids = positionDao
					.allPersons(positionDao.findByPrimaryKey(deUnit.getDn().toString(), positionId));

			list.addAll(pids);
		} catch (Exception e) {
			wrapException(e);
		}
		return list;
	}

	protected List<String> groupPersons(String organizationId, String groupId) {
		List<String> list = new ArrayList<String>();
		try {
			if (StringUtils.isBlank(groupId))
				return list;

			validateOrganization(organizationId);
			OrganizationUnit deUnit = gainOrganizationUnit(organizationId, Constants.GROUP);
			validateGroup(deUnit.getDn().toString(), groupId);

			List<String> pids = groupDao.allAttrName(
					groupDao.findByPrimaryKey(deUnit.getDn().toString(), groupId).getDn(), Constants.PERSONS);

			// groupDao.allPersons(groupDao.findByPrimaryKey(
			// deUnit.getDn().toString(), groupId));

			list.addAll(pids);
		} catch (Exception e) {
			wrapException(e);
		}
		return list;
	}

	protected List<String> allPersonIds(List<Person> list) {
		List<String> origs = new ArrayList<String>();
		for (Person person : list) {
			origs.add(person.getUid());
		}
		return origs;
	}

	protected List<String> formIds(String personDns) {
		List<String> forms = new ArrayList<String>();
		String[] nn = StringUtils.split(personDns, ";");
		if (nn == null)
			nn = new String[0];
		for (String string : nn) {
			forms.add(string);
		}
		return forms;
	}

	protected List<String> addIds(List<String> origs, List<String> forms) {
		List<String> adds = new ArrayList<String>();

		for (String string : forms) {
			if (!origs.contains(string)) {
				// 新增的
				adds.add(string);
			}
		}

		return adds;
	}

	protected List<String> removeIds(List<String> origs, List<String> forms) {
		List<String> rems = new ArrayList<String>();

		for (String string : origs) {
			if (!forms.contains(string)) {
				rems.add(string);
			}
		}

		return rems;
	}

	@Override
	public void remove(String dn) {
		String[] dns = StringUtils.split(dn, ";");
		if (dns == null)
			return;

		for (String string : dns) {
			DirContextOperations context = ldapTemplate.lookupContext(string);

			context.setAttributeValue(Constants.DELETED, Constants.TRUE);
			ldapTemplate.modifyAttributes(context);
		}
	}

	public List<? extends SoaTop> sortResult(List<? extends SoaTop> list) {
		if (list == null)
			return list;

		try {
			// 排序
			Collections.sort(list, new Comparator<SoaTop>() {
				@Override
				public int compare(SoaTop o1, SoaTop o2) {
					return (getPriority(o1) - getPriority(o2));
				}
			});
		} catch (Exception e) {
			printException(e);
		}

		return list;

	}

	protected int getPriority(SoaTop soaTop) {
		try {
			return Integer.valueOf(BeanUtils.getProperty(soaTop, Constants.PRIORITY));
		} catch (Exception e) {
			if (log.isDebugEnabled())
				log.debug("获取排序字段值出错", e);
		}
		return 0;
	}

	@Override
	public void sort(String dns) {
		String[] dnss = StringUtils.split(dns, ";");
		if (dnss == null)
			return;

		try {
			int i = 1;
			for (String str : dnss) {
				DirContextOperations context = ldapTemplate.lookupContext(str);

				context.setAttributeValue(Constants.PRIORITY, String.valueOf(i++));
				ldapTemplate.modifyAttributes(context);
			}
		} catch (Exception e) {
			wrapException(e);
		}

	}

	@Override
	public List<Department> findDepartments(String organizationId, String attrName, String dn) {
		List<Department> results = new ArrayList<Department>();
		try {
			if (StringUtils.isBlank(dn))
				return results;
			OrganizationUnit unit = gainOrganizationUnit(organizationId, Constants.DEPARTMENT);
			return departmentDao.findAllByAttr(unit.getDn().toString(), defaultSearchScope, attrName, dn);

		} catch (Exception e) {
			printException(e);
		}
		return results;
	}
	
	@Override
	public List<Account> findAccounts(String organizationId, String attrName, String dn) {
		List<Account> results = new ArrayList<Account>();
		try {
			if (StringUtils.isBlank(dn))
				return results;
			OrganizationUnit unit = gainOrganizationUnit(organizationId, Constants.ACCOUNT);
			return accountDao.findAllByAttr(unit.getDn().toString(), defaultSearchScope, attrName, dn);

		} catch (Exception e) {
			printException(e);
		}
		return results;
	}
	
	@Override
	public List<Account> findAccounts_region(String regionDn, String attrName, String dn) {
		List<Account> results = new ArrayList<Account>();
		try {
			if (StringUtils.isBlank(dn))
				return results;
			OrganizationUnit unit = gainOrganizationUnit_region(regionDn, Constants.REGION);
			return accountDao.findAllByAttr(unit.getDn().toString(), defaultSearchScope, attrName, dn);

		} catch (Exception e) {
			printException(e);
		}
		return results;
	}

	boolean addUnit(String organizationId, String departmentId, String ids, String unitOu)
			throws OrganizationException {
		try {
			validateOrganization(organizationId);

			OrganizationUnit deUnit = gainOrganizationUnit(organizationId, Constants.DEPARTMENT);
			validateDepartment(deUnit.getDn().toString(), departmentId);

			Department department = departmentDao.findByPrimaryKey(deUnit.getDn().toString(), departmentId);

			String[] pids = StringUtils.split(ids, ",");
			for (String pid : pids) {
				OrganizationUnit unit = gainOrganizationUnit(organizationId, unitOu);

				if (Constants.PERSON.equals(unitOu)) {
					validatePerson(unit.getDn().toString(), pid);
					departmentDao.addPersons(department, personDao.findByPrimaryKey(unit.getDn().toString(), pid));

				} else if (Constants.POSITION.equals(unitOu)) {
					validatePosition(unit.getDn().toString(), pid);
					departmentDao.addPositions(department, positionDao.findByPrimaryKey(unit.getDn().toString(), pid));

				} else if (Constants.GROUP.equals(unitOu)) {
					validateGroup(unit.getDn().toString(), pid);
					departmentDao.addGroups(department, groupDao.findByPrimaryKey(unit.getDn().toString(), pid));

				} else if (Constants.ROLE.equals(unitOu)) {
					validateRole(unit.getDn().toString(), pid);
					departmentDao.addRoles(department, roleDao.findByPrimaryKey(unit.getDn().toString(), pid));

				}
			}

			return true;
		} catch (Exception e) {
			wrapException(e);
		}
		return false;
	}

	boolean removeUnit(String organizationId, String departmentId, String ids, String unitOu)
			throws OrganizationException {
		try {
			validateOrganization(organizationId);

			OrganizationUnit deUnit = gainOrganizationUnit(organizationId, Constants.DEPARTMENT);
			validateDepartment(deUnit.getDn().toString(), departmentId);

			Department department = departmentDao.findByPrimaryKey(deUnit.getDn().toString(), departmentId);

			String[] pids = StringUtils.split(ids, ",");
			for (String pid : pids) {
				OrganizationUnit unit = gainOrganizationUnit(organizationId, unitOu);

				if (Constants.PERSON.equals(unitOu)) {
					validatePerson(unit.getDn().toString(), pid);
					departmentDao.removePersons(department, personDao.findByPrimaryKey(unit.getDn().toString(), pid));

				} else if (Constants.POSITION.equals(unitOu)) {
					validatePosition(unit.getDn().toString(), pid);
					departmentDao.removePositions(department,
							positionDao.findByPrimaryKey(unit.getDn().toString(), pid));

				} else if (Constants.GROUP.equals(unitOu)) {
					validateGroup(unit.getDn().toString(), pid);
					departmentDao.removeGroups(department, groupDao.findByPrimaryKey(unit.getDn().toString(), pid));

				} else if (Constants.ROLE.equals(unitOu)) {
					validateRole(unit.getDn().toString(), pid);
					departmentDao.removeRoles(department, roleDao.findByPrimaryKey(unit.getDn().toString(), pid));

				}
			}

			return true;
		} catch (Exception e) {
			wrapException(e);
		}
		return false;
	}

	void ldapAddAttrName(List<String> addDns, String attrName, String attrValue) {
		for (String string : addDns) {
			DirContextOperations context = ldapTemplate.lookupContext(string);
			context.addAttributeValue(attrName, attrValue);
			ldapTemplate.modifyAttributes(context);
		}
	}

	void ldapRemoveAttrName(List<String> remDns, String attrName, String attrValue) {
		for (String string : remDns) {
			DirContextOperations context = ldapTemplate.lookupContext(string);
			context.removeAttributeValue(attrName, attrValue);
			ldapTemplate.modifyAttributes(context);
		}
	}

	List<String> allDepartmentsDns(List<Department> list) {
		List<String> result = new ArrayList<String>();
		try {
			for (Department string : list) {
				result.add(string.getDn().toString());
			}
		} catch (Exception e) {
			printException(e);
		}
		return result;
	}
	
	List<String> allAccountsDns(List<Account> list) {
		List<String> result = new ArrayList<String>();
		try {
			for (Account string : list) {
				result.add(string.getDn().toString());
			}
		} catch (Exception e) {
			printException(e);
		}
		return result;
	}

	List<String> allPositionsDns(List<Position> list) {
		List<String> result = new ArrayList<String>();
		try {
			for (Position string : list) {
				result.add(string.getDn().toString());
			}
		} catch (Exception e) {
			printException(e);
		}
		return result;
	}

	List<String> allGroupsDns(List<Group> list) {
		List<String> result = new ArrayList<String>();
		try {
			for (Group string : list) {
				result.add(string.getDn().toString());
			}
		} catch (Exception e) {
			printException(e);
		}
		return result;
	}

	protected List<Organization> oneOrg(String organizationId) {
		List<Organization> result = new ArrayList<Organization>();
		try {
			Organization organization = findOrganization(organizationId);
			result.add(organization);

			List<Organization> list = organizationDao.allOrganizations(organization.getDn().toString(),
					defaultSearchScope, null);
			sortResult(list);

			for (Organization or : list) {
				result.addAll(oneOrg(or.getBid()));
			}
		} catch (Exception e) {
			printException(e);
		}
		return result;
	}

	@Override
	public Change change(String organizationId, SoaTop st) {
		return new Change(organizationId);
	}

	public void update(String organizationId, SoaTop st) {
		changeMgrService.record(change(organizationId, st));
	}
}
